Type MaxMod_Sample

	Field Name			:String			' name of the sample
	Field Length		:Int			' in samples
	Field SoundData		:TBank			' bank that contains the sound data
	Field LoopStart		:Int			' start of the loop in samples
	Field LoopLength	:Int			' length of the loop in samples
	Field Loop			:Int			' 0 = no loop, 1 = loop, 2 = pingpong loop
	Field Volume		:Float = 1		' this samples default volume						      0 .. +1
	Field FineTune		:Double			' this samples finetune value						-1 .. 0 .. +1
	Field RelativeNote	:Int			' for XMs, this gets added to the tracks note
	Field Pan			:Int			' default pan for this sample
	Field Flag			:Int			' 
'	Field Reserved		:Int			' uh, reserved :)
	Field C2SPD			:Int			' for s3m

	Field fp			:Int Ptr
	
	' -------------------------------------------------------------------------------------------------

	Function Load:MaxMod_Sample(f:TStream)
		Local This:MaxMod_Sample 	= New MaxMod_Sample
		Local l:Int 				= ReadByte(f)
		This.Name 					= ReadString(f,l)
'		Print "loading sample... ~q"+This.Name+"~q"
		This.Length 				= ReadInt(f)
		This.LoopStart 				= ReadInt(f)
		This.LoopLength 			= ReadInt(f)
		This.Loop 					= ReadInt(f)
		This.Volume 				= ReadFloat(f)
		This.FineTune 				= ReadFloat(f)
		This.RelativeNote 			= ReadInt(f)
		This.Pan 					= ReadInt(f)
		This.Flag 					= ReadInt(f)
'		This.Reserved 				= ReadInt(f)
		This.C2SPD 					= ReadInt(f)
		
		Local size:Int 				= ReadInt(f)
		Local Data:TBank			= CreateBank(Size)
		Local i:Int, bptr:Short Ptr = Short Ptr(BankBuf(Data))
		For i=0 Until This.Length
			bptr[i] = ReadShort(f)
		Next
		This.ConvertSampleFromBank(data, 2)

		Return This
	EndFunction
	
	' -------------------------------------------------------------------------------------------------
	
	Method Save(f:TStream)
'		Print "saving sample... ~q"+Name+"~q"
		WriteByte f,Len(Name)
		WriteString f,Name
		WriteInt f,Length
		WriteInt f,LoopStart
		WriteInt f,LoopLength
		WriteInt f,Loop
		WriteFloat f,Volume
		WriteFloat f,FineTune
		WriteInt f,RelativeNote
		WriteInt f,Pan
		WriteInt f,Flag
'		WriteInt f,Reserved
		WriteInt f,C2SPD
		WriteInt f,BankSize(SoundData)/2		
		Local i:Int
		For i=0 Until Length
			WriteShort f,fp[i]
		Next
	EndMethod
	
	' -------------------------------------------------------------------------------------------------
	
	Method ConvertSampleFromBank(Source:TBank, Format:Int=1)

		Local Pos:Int, V:Float, V1:Float, S:Double
		Select Format
		
			' 8bit mono
			Case 1
				Length    			= Source.Size()
				SoundData 			= CreateBank(Length*4)
				Local bp:Byte Ptr 	= Byte Ptr(BankBuf(Source))
				Local dbp:Int Ptr   = Int Ptr(BankBuf(SoundData))
				s = 32768!/128
				For pos=0 Until Length
					v = bp[pos]
					If v=>128 Then v:-256
					dbp[pos] = (v*s)+0.5
				Next
				
			' 16bit mono
			Case 2
				Length    			= Source.Size()/2
				SoundData 			= CreateBank(Length*4)
				Local bp:Short Ptr 	= Short Ptr(BankBuf(Source))
				Local dbp:Int Ptr   = Int Ptr(BankBuf(SoundData))
				For pos=0 Until Length
					v = bp[pos]
					If V=>32768 Then V:-65536 
					dbp[pos] = v
				Next

			' 8bit stereo		(mixes down to mono)
'			Case 3

			' 16bit stereo		(mixes down to mono)
			Case 4
				Length    			= (Source.Size()/2)/2
				SoundData 			= CreateBank(Length*4)
				Local bp:Short Ptr 	= Short Ptr(BankBuf(Source))
				Local dbp:Int Ptr   = Int Ptr(BankBuf(SoundData))
				Local rpos:Int
				For pos=0 Until Length
					v = bp[rpos] ; rpos:+1
					If V=>32768 Then V:-65536
					v:/2
					v1 = bp[rpos] ; rpos:+1
					If V1=>32768 Then V1:-65536 
					v1:/2
					dbp[pos] = (v+v1)+0.5
				Next

		EndSelect
		
		fp = Int Ptr(SoundData.Buf())
	
	EndMethod

	' -------------------------------------------------------------------------------------------------

	Method GetAV:Double(Pos:Double,Add:Double)
		If Not Add Return 0
		Local p1:Double
		If Add=>0
			p1 = FixPos(Pos+1,Add)
			Return fp[Pos]+((p1-fp[Pos])*(pos-Int(pos)))
'			Return Interpolate_Cosine(fp[Floor(Pos)], p1, Pos)
			
		Else
			p1 = FixPos(Pos-1,Add)
			Return p1+((fp[Pos]-p1)*(pos-Int(pos)))
'			Return -Interpolate_Cosine(p1, fp[Floor(Pos)], Pos)
		EndIf
		
	EndMethod

	' -------------------------------------------------------------------------------------------------

	' if apply = false then returns av for position
	' if apply = true then returns the new position
	Method FixPos:Double(Pos:Double,vel:Double Var, Apply:Int=False)
		If vel=0 Return -1
		Select Loop
			Case 1	' LOOP
				Select True
					Case pos<0 							; pos = -1
					Case pos>loopstart+looplength
						If looplength=0
							pos = -1 ; If Apply Then Vel=0
						Else
							pos = loopstart+((pos-(loopstart+looplength)) Mod looplength) 
						EndIf
					Case pos=>length 	 				; pos = loopstart+((pos-length) Mod (length-loopstart))
				EndSelect
			Case 2	' PINGPONG
				If vel>0
					Select True
						Case pos<0 						; pos = -1 ; If Apply Then Vel=0
						Case pos>loopstart+looplength
							If looplength=0
								pos = -1 ; If Apply Then Vel=0
							Else
								If Apply Then vel=-vel
								pos = FixPos( (loopstart+looplength)-(pos-(loopstart+looplength)), vel,  Apply) 
							EndIf
						Case pos=>length				; pos = length-(pos-length) ; If Apply Then vel=-vel
					EndSelect
				Else
					Select True
						'Case pos<0 						; pos = Abs(pos) ; If Apply Then vel=Abs(vel)
						Case pos<loopstart
							If Apply Then vel=Abs(vel)
							pos = FixPos( loopstart+Abs(pos-loopstart), vel, Apply)
					EndSelect
				EndIf
			Default	' NO LOOP
				Select True
					Case pos=>length					; pos = -1 ; If Apply Then Vel=0
					Case pos<0 							; pos = -1 ; If Apply Then Vel=0
				EndSelect
		EndSelect
		
		If apply
			Return pos
		Else
			If pos=>0 Return fp[Floor(pos)]
		EndIf
		
	EndMethod

EndType
